import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;

import de.jmda.core.mproc.ProcessingUtilities;

public class IsA
{
	public /*final static*/ TypeElement COLLECTION = ProcessingUtilities.getTypeElement(Collection.class);
	public /*final static*/ TypeElement MAP = ProcessingUtilities.getTypeElement(Map.class);
	public /*final static*/ TypeElement VOID = ProcessingUtilities.getTypeElement(Void.class);
	public /*final static*/ WildcardType WILDCARD_TYPE_NULL = ProcessingUtilities.getWildcardType(null, null);
	public /*final static*/ Map<String,DeclaredType> CACHED_PARENT_TYPES = new HashMap<String, DeclaredType>();

	public /*static*/ boolean isA(TypeMirror type, TypeElement typeElement)
	{
		// Have we used this type before?
		DeclaredType parentType =
		    CACHED_PARENT_TYPES.get(typeElement.getQualifiedName().toString());
		if (parentType == null)
		{
			// How many generic type parameters does this typeElement require?
			int genericsCount = typeElement.getTypeParameters().size();

			// Fill the right number of types with nulls
			TypeMirror[] types = new TypeMirror[genericsCount];
			for (int i = 0; i < genericsCount; i++)
			{
				types[i] = WILDCARD_TYPE_NULL;
			}

			// Locate the correct DeclaredType to match with the type
			parentType = ProcessingUtilities.getDeclaredType(typeElement, types);

			// Remember this DeclaredType
			CACHED_PARENT_TYPES.put(typeElement.getQualifiedName().toString(), parentType);
		}

		// Is the given type able to be assigned as the typeElement?
		return ProcessingUtilities.isAssignableType(type, parentType);
	}
}
